﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Input;
using System.Xml.Linq;
using System.ComponentModel;
using System.Windows.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;

namespace YouTube
{
	public static class YouTubeProvider
	{
		private static Regex resultRegex = new Regex(@"<a class=""video-thumb-120"" href=""\/watch\?v=(?<id>[^""]+)""\s*>\s*<img title=""(?<title>[^""]+)""[^><]+src=""(?<src>[^""]+)""([^><]+thumb=""(?<thumb>[^""]+)"")?", RegexOptions.Singleline | RegexOptions.Compiled);

		private static XNamespace atom = "http://www.w3.org/2005/Atom";
		private static XNamespace app = "http://www.w3.org/2007/app";
		private static XNamespace media = "http://search.yahoo.com/mrss/";
		private static XNamespace openSearch = "http://a9.com/-/spec/opensearch/1.1/";
		private static XNamespace gd = "http://schemas.google.com/g/2005";
		private static XNamespace yt = "http://gdata.youtube.com/schemas/2007";

		public static void SearchAsync(string query, Action<IEnumerable<YouTubeVideo>,Exception> callback)
		{
			Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

			ThreadPool.QueueUserWorkItem((o) =>
				{
					try
					{
						string resultPage;
						using(WebClient client = new WebClient())
						{
							string escapedQuery = Uri.EscapeDataString(query);
							//resultPage = client.DownloadString("http://www.youtube.com/results?search_type=videos&search_query=" + escapedQuery);
							resultPage = client.DownloadString("http://gdata.youtube.com/feeds/api/videos?v=2&max-results=20&q=" + escapedQuery);
						}


						dispatcher.BeginInvoke((Action)delegate
						{
							try
							{
								XDocument document = XDocument.Parse(resultPage);
								var videos = (from entry in document.Root.Elements(atom + "entry")
											  let mediaGroup = entry.Element(media + "group")
											  select new YouTubeVideo(
												  id: mediaGroup.Element(yt + "videoid").Value,
												  title: entry.Element(atom + "title").Value,
												  thumbUri: new Uri(mediaGroup.Elements(media + "thumbnail").Last().Attribute("url").Value)
											  ));
								callback(videos, null);
							}
							catch(Exception ex)
							{
								callback(null, ex);
							}
						});
					}
					catch(Exception ex)
					{
						dispatcher.BeginInvoke(callback, null, ex);
					}
				});

			//List<YouTubeVideo> videos = new List<YouTubeVideo>();
			//foreach(Match resultMatch in resultRegex.Matches(resultPage))
			//{
			//    string id = resultMatch.Groups["id"].Value;
			//    string title = resultMatch.Groups["title"].Value;
			//    Uri thumbUri;
			//    if(resultMatch.Groups["thumb"].Success)
			//        thumbUri = new Uri(resultMatch.Groups["thumb"].Value);
			//    else
			//        thumbUri = new Uri(resultMatch.Groups["src"].Value);

			//    videos.Add(new YouTubeVideo(id, title, thumbUri));
			//}
			//return videos.ToArray();
		}


		//private static Regex videoKeyRegex = new Regex(@"""t"": ""(?<key>[^""]+)""", RegexOptions.Singleline | RegexOptions.Compiled);
		private static Regex videoKeyRegex = new Regex("&t=(?<key>[^&]+)&", RegexOptions.Singleline | RegexOptions.Compiled);

		// "fmt_list": "22%2F2000000%2F9%2F0%2F115%2C35%2F640000%2F9%2F0%2F115%2C18%2F512000%2F9%2F0%2F115%2C34%2F0%2F9%2F0%2F115%2C5%2F0%2F7%2F0%2F0"
		//private static Regex videoFormatsRegex = new Regex(@"""fmt_list"": ""(?<formats>[^""]+)""", RegexOptions.Singleline | RegexOptions.Compiled);
		//  &fmt_list=22%2F2000000%2F9%2F0%2F115%2C35%2F640000%2F9%2F0%2F115%2C34%2F0%2F9%2F0%2F115%2C5%2F0%2F7%2F0%2F0&
		private static Regex videoFormatsRegex = new Regex("&fmt_list=(?<formats>[^&]+)&", RegexOptions.Singleline | RegexOptions.Compiled);

		/// <summary>Gets file's URI of the video.</summary>
		/// <param name="id">The id of the video.</param>
		/// <param name="fileUri">The returned file URI.</param>
		/// <param name="previewFileUri">The returned preview file URI.</param>
		public static void GetVideoFileUri(string id, out Uri previewFileUri, out Uri fileUri)
		{
			string videoPage;
			using(WebClient client = new WebClient())
				videoPage = client.DownloadString("http://www.youtube.com/watch?v=" + id);

			Match match = videoKeyRegex.Match(videoPage);
			if(match.Success)
			{
				string key = match.Groups["key"].Value;

				// MP4 formats:
				//  18: 480×360 (iPhone)
				//  22: 720p
				//  37: 1080p
				int[] mp4formats = new int[] { 18, 22, 37 };
				int format = -1;

				match = videoFormatsRegex.Match(videoPage);
				if(match.Success)
				{
					string formats = match.Groups["formats"].Value.Replace("%2F", "/").Replace("%2C", ",");
					format = (from f in formats.Split(',') select int.Parse(f.Split('/')[0])).Intersect(mp4formats).DefaultIfEmpty(-1).Max();
				}

				if(format > -1)
				{
					previewFileUri = new Uri(string.Format("http://www.youtube.com/get_video?video_id={0}&t={1}&fmt=18", id, key));
					fileUri = new Uri(string.Format("http://www.youtube.com/get_video?video_id={0}&t={1}&fmt={2}", id, key, format));
					return;
				}
			}

			previewFileUri = null;
			fileUri = null;
		}
	}

	public class YouTubeVideo : DependencyObject
	{
		public string Id { get; private set; }
		public string Title { get; private set; }
		public Uri ThumbUri { get; private set; }



		public Uri PreviewFileUri
		{
			get { return (Uri)GetValue(PreviewFileUriProperty); }
			set { SetValue(PreviewFileUriProperty, value); }
		}
		// Using a DependencyProperty as the backing store for PreviewFileUri.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty PreviewFileUriProperty = DependencyProperty.Register("PreviewFileUri", typeof(Uri), typeof(YouTubeVideo), new UIPropertyMetadata(null));

		public Uri FileUri
		{
			get { return (Uri)GetValue(FileUriProperty); }
			set { SetValue(FileUriProperty, value); }
		}
		// Using a DependencyProperty as the backing store for FileUri.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty FileUriProperty = DependencyProperty.Register("FileUri", typeof(Uri), typeof(YouTubeVideo), new UIPropertyMetadata(null));

		//public BitmapSource Thumbnail
		//{
		//    get { return (BitmapSource)GetValue(ThumbnailProperty); }
		//    set { SetValue(ThumbnailProperty, value); }
		//}
		//// Using a DependencyProperty as the backing store for Thumbnail.  This enables animation, styling, binding, etc...
		//public static readonly DependencyProperty ThumbnailProperty = DependencyProperty.Register("Thumbnail", typeof(BitmapSource), typeof(YouTubeVideo), new UIPropertyMetadata(null));


		public YouTubeVideo(string id, string title, Uri thumbUri)
		{
			Id = id;
			Title = title;
			ThumbUri = thumbUri;

			ThreadPool.QueueUserWorkItem((o) => Init());
		}

		public void Init()
		{			
			// Get video file URL
			Uri previewFileUri, fileUri;
			YouTubeProvider.GetVideoFileUri(Id, out previewFileUri, out fileUri);

			// Get thumbnail bytes
			//byte[] data = new WebClient().DownloadData(ThumbUri);

			Action apply = () =>
				{
					PreviewFileUri = previewFileUri;
					FileUri = fileUri;

					//// Construct thumbnail image
					//using(MemoryStream ms = new MemoryStream(data))
					//{
					//    var thumb = new BitmapImage();
					//    thumb.BeginInit();
					//    thumb.StreamSource = ms;
					//    thumb.EndInit();
					//    Thumbnail = thumb;
					//}
				};


			// Perform apply operations back in the Dispatcher if necessary
			if(Dispatcher.CheckAccess())
				apply();
			else
				Dispatcher.BeginInvoke(apply);
		}
	}
}
